import { Context, Next } from 'koa'
import OpenAi from 'openai'
import fs from 'fs'
import GptUser from '../models/gptUser'
import common from './common'
import gptDialog from '../models/gptDialog'
import gptUser from '../models/gptUser'
import gptRobot from '../models/gptRobot'
import crypto from 'crypto'
import redis from '../db/redis'
import { GPTINFO } from '../config/constant'
import { ChatCompletionMessageParam } from 'openai/resources/chat/completions'
import gptFaceback from '../models/gptFaceback'
import { PassThrough } from "stream"
import COS from 'cos-nodejs-sdk-v5'
import { TENCENT_COS } from '../config/constant'
const API_KEY = 'sk-xsAp4gBwl3IxYYLz0d3961DeA9A1462eA7Ea2bE137A12308'
const openai = new OpenAi({
  apiKey: API_KEY,
  baseURL: 'https://openkey.cloud/v1' //如果是代理的key，就写地址，如果是官网的就不用写
})

// const a = [
//   JSON.stringify({
//     avatar:
//       'https://bucket-chabaidao-1308160773.cos.ap-guangzhou.myqcloud.com/img/1720936822821/.svg',
//     content: '你好',
//     date: 1720975866047,
//     role: 'user'
//   }),
//   JSON.stringify({
//     avatar: 'https://bucket-chabaidao-1308160773.cos.ap-guangzhou.myqcloud.com/img%2Frobot.png',
//     name: 'robot',
//     content:
//       '您好！有什么可以帮助您的吗？如果您有任何问题，请随时向我提问。我会尽力帮助您解决问题。',
//     role: 'assistant',
//     prompt: null,
//     date: 1720976453070
//   })
// ]
// const b = new Array(50).fill(a)
// redis.lpush(`session:{6696efe6d135132e573e9357}`, ...b)
// for (let i = 0; i < 100; i++) {
//   redis.lpop(`session:{669486ca1005993ef7377a8d}`)

// }
async function generateText(messages: ChatCompletionMessageParam[], ctx: Context) {
  ctx.status = 200
  const streamData = new PassThrough();
  ctx.body = streamData;
  let content = ""
  const id = crypto.randomUUID()
  redis.setex(id, 60, JSON.stringify(streamData))
  console.log("1", Date.now());
  streamData.write(
    `data: ${JSON.stringify({
      message: "",
      id,
      event: "req"
    })}\n\n`
  );

  if (ctx.isUpload) {
    const completion = await openai.chat.completions.create({
      model: 'gpt-4o',
      messages: [messages[0]],
      max_tokens: 300,
    })
    const _content = completion.choices[0].message?.content || "我休息一下"
    streamData.write(
      `data: ${JSON.stringify({
        message: _content,
        event: "text"
        , id
      })}\n\n`
    );
    streamData.write(
      `data: ${JSON.stringify({
        message: "",
        event: "end"
        , id
      })}\n\n`
    );
    return _content
  } else {
    const completion = await openai.chat.completions.create({
      model: 'gpt-3.5-turbo',
      // messages数组上下文联系，上文，当前文
      messages: messages.reverse(),
      stream: true
    })
    console.log("2", Date.now());


    for await (const chunk of completion) {
      if (chunk) {
        const _content = chunk['choices'][0].delta.content || ''
        if (chunk['choices'][0].finish_reason === "stop") {
          streamData.write(
            `data: ${JSON.stringify({
              message: "",
              event: "end"
              , id
            })}\n\n`
          );
        } else {
          content += _content;
          streamData.write(
            `data: ${JSON.stringify({
              message: _content,
              event: "text"
              , id
            })}\n\n`
          );
        }
        console.log("2", Date.now());
      }
    }
    return content
  }
}
class GptController {
  // 获取用户信息
  async getUserInfo(ctx: Context, next: Next) {
    const data = await GptUser.findById(ctx.userInfo._id)
    ctx.body = data
    next()
  }
  // 修改用户信息
  async updateUserInfo(ctx: Context, next: Next) {
    ctx.isUpload = true
    const { url } = (await common.upload(ctx, next)) as { url: string }
    await GptUser.updateOne({ _id: ctx.userInfo._id }, { avatar: url })
    ctx.body = { avatar: url }
    next()
  }
  // 新建会话
  async createDialog(ctx: Context, next: Next) {
    const { robot_id } = ctx.request.body
    const params = { user_id: ctx.userInfo._id, robot_id, title: '未命名会话' }
    if (robot_id) {
      const _data = await gptRobot.aggregate([
        {
          $lookup: {
            from: 'GptRobot',
            localField: 'robot_id',
            foreignField: '_id',
            as: 'robot'
          }
        },
        { $unwind: '$robot' },
        {
          $project: {
            prologue: 1,
            prompt: 1,
            model: 1,
            name: 1
          }
        }
      ])
      params.title = _data[0].name
      GPTINFO.prompt = _data[0].prompt
    }
    const data = await gptDialog.create(params)
    await gptUser.updateOne({ _id: ctx.userInfo._id }, { last_dialog_id: data._id })
    GPTINFO.date = Date.now()
    GPTINFO.content = "我是一个AI助手，我擅长回答各种问题，你可以问我任何问题，我会给你一个答案。"
    await redis.lpush(`session:{${data._id}}`, JSON.stringify(GPTINFO))
    ctx.body = { list: [data], page: 1, size: 1, total: 1, message: GPTINFO }
    next()
  }

  // 获取会话列表
  async getDialogList(ctx: Context, next: Next) {
    const { page = 1, size = 20 } = ctx.request.query
    const data = await gptDialog
      .find({ user_id: ctx.userInfo._id })
      .skip((Number(page) - 1) * Number(size))
      .limit(Number(size))
    const total = await gptDialog.countDocuments({ user_id: ctx.userInfo._id })
    ctx.body = {
      list: data,
      total,
      page,
      size
    }
    next()
  }
  // 删除会话
  async deleteDialog(ctx: Context, next: Next) {
    const { id } = ctx.params
    ctx.verifyParams({
      id: {
        type: 'string'
      }
    })
    await gptDialog.deleteOne({ _id: id })
    await redis.del(`session:{${id}}`)
    ctx.body = '删除成功'
    next()
  }
  // 编辑话题
  async editDialog(ctx: Context, next: Next) {
    const { id, title } = ctx.request.body
    ctx.verifyParams({
      id: {
        type: 'string'
      },
      title: {
        type: 'string'
      }
    })
    await gptDialog.updateOne({ _id: id }, { title })
    ctx.body = '编辑成功'
    next()
  }
  // 获取会话消息
  async getDialogMessage(ctx: Context, next: Next) {
    const { id, page = 1, size = 10 } = ctx.request.query
    ctx.verifyParams({
      id: {
        type: 'string'
      }
    })
    const total = await redis.llen(`session:{${id}}`)
    const offset = (Number(page) - 1) * Number(size)

    const data = (await redis.lrange(`session:{${id}}`, offset, Number(size) + offset - 1)).map(
      (item) => JSON.parse(item)
    )
    await gptUser.updateOne({ _id: ctx.userInfo._id }, { last_dialog_id: id })
    ctx.body = { list: data, total, page: Number(page), size: Number(size) }
    next()
  }

  // 聊天
  async chat(ctx: Context, next: Next) {
    console.log(Date.now());

    ctx.type = "text/event-stream"
    ctx.set({
      "Transfer-Encoding": "chunked",
      "Content-Type": "text/event-stream",
      "X-Accel-Buffering": "no",
      "Cache-Control": "no-cache",
      "Connection": "keep-alive",
      "Access-Control-Allow-Origin": "*",
      "Vary": "Accept-Encoding"
    })
    // prompt 附加信息 {type:"text",content:""} // {type:"file",content:"key..."} // {type:"image",content:"url..."}]
    const { message, dialog_id, type = 0, file } = ctx.request.body

    ctx.verifyParams({
      dialog_id: {
        type: 'string'
      },

      message: {
        type: 'string',
        required: false
      }
    })

    const isExist = await redis.exists(`session:{${dialog_id}}`)
    if (!isExist) {
      throw '会话id不存在'
    } else {
      const messages = (await redis.lrange(`session:{${dialog_id}}`, 0, 100)).map((item) =>
        JSON.parse(item)
      )
      if (messages.length === 1) {
        await gptDialog.updateOne({ _id: dialog_id }, { title: message.slice(0, 10) })
      }

      if (type === 0) {
        //用户提问
        const _message = {
          avatar: ctx.userInfo.avatar,
          content: message,
          date: Date.now(),
          role: 'user'
        }
        await redis.lpush(`session:{${dialog_id}}`, JSON.stringify(_message))
        messages.unshift(_message)
      } else if (type === 1) {
        // 用户编辑重发
        await redis.lpop(`session:{${dialog_id}}`)
        messages.shift()
        messages[0].content = message
        messages[0].date = Date.now()
        await redis.lset(`session:{${dialog_id}}`, 0, JSON.stringify(messages[0]))
      } else if (type === 2) {
        // 重新回答
        await redis.lset(
          `session:{${dialog_id}}`,
          0,
          JSON.stringify({ ...messages[0], is_revocation: true, date: Date.now() })
        )
      } else if (Number(type) === 3) {
        console.log(file);

        // 用户附带文件等消息询问
        if (!file) {
          throw '文件不能为空'
        }
        ctx.isUpload = true

        if (file.fileType == "image") {
          const _message = {
            avatar: ctx.userInfo.avatar,
            content: [
              { type: 'text', text: message },
              {
                type: 'image_url',
                image_url: {
                  "url": file.url,
                  "detail": "low"
                  // url: `data:image/png;base64,${fs.readFileSync(file.filepath).toString('base64')}`
                }
              }
            ],
            prompt: { type: 'image', content: file.url },
            date: Date.now(),
            role: 'user',
          }

          await redis.lpush(`session:{${dialog_id}}`, JSON.stringify({ ..._message, content: message }))
          messages.unshift(_message)
        } else {
          const { SecretId, SecretKey, Bucket, Region, prefix } = TENCENT_COS
          const cos = new COS({
            SecretId,
            SecretKey
          })
          const res = await cos.getObject({
            Bucket,
            Region,
            Key: file.key,
          })
          const _message = {
            avatar: ctx.userInfo.avatar,
            content: [
              { type: 'text', message },
              {
                type: 'text',
                message: res.Body
              }
            ],
            prompt: { type: "file", content: file.url },
            date: Date.now(),
            role: 'user'
          }
          await redis.lpush(`session:{${dialog_id}}`, JSON.stringify({ ..._message, content: message }))
          messages.unshift(_message)
        }
      } else {
        throw '错误的type传参'
      }

      const assistant_msg = await generateText(messages, ctx)

      GPTINFO.content = assistant_msg!
      GPTINFO.date = Date.now()
      await redis.lpush(`session:{${dialog_id}}`, JSON.stringify(GPTINFO))
    }
  }

  // 用户提交反馈
  async addFaceback(ctx: Context, next: Next) {
    const { star, content } = ctx.request.body
    await gptFaceback.create({ user_id: ctx.userInfo._id, star, content })
    ctx.body = '提交成功'
    next()
  }
  // 聊天信息编辑
  // async messageEdit(ctx: Context, next: Next) {
  //   const { dialog_id, content } = ctx.request.body
  //   await redis.lpop(`session:{${dialog_id}}`)
  //   const messages = (await redis.lrange(`session:{${dialog_id}}`, 0, 100)).map((item) =>
  //     JSON.parse(item)
  //   )
  //   messages[0].content = content
  //   messages[0].date = Date.now()
  //   await redis.lset(`session:{${dialog_id}}`, 1, JSON.stringify(messages[0]))
  //   ctx.status = 200
  //   const assistant_msg = await generateText(messages.reverse(), ctx)
  //   GPTINFO.content = assistant_msg
  //   GPTINFO.date = Date.now()
  //   await redis.lpush(`session:{${dialog_id}}`, JSON.stringify(GPTINFO))

  //   ctx.res.end()
  // }

  //   翻译
  // async translate(ctx: Context, next: Next) {
  //   const { message, to } = ctx.request.body as { message: string; to: keyof typeof Langs }
  //   if (Langs[to]) {
  //     const mes = `Translate the following text to ${Langs[to]}: ${message}`
  //     ctx.status = 200
  //     await generateText(mes, ctx.res)
  //   } else {
  //     ctx.status = 500
  //   }
  // }

  // // 纠错
  // async correct(ctx: Context, next: Next) {
  //   const { message } = ctx.query
  //   const mes = `文本纠正："${message}"`
  //   console.log(mes)
  //   ctx.status = 200
  //   await generateText(mes, ctx.res)
  // }

  //   图片生成
  async createImg(ctx: Context, next: Next) {
    const { message } = ctx.request.body
    if (!message) return
    const image = await openai.images.generate({
      model: 'dall-e-2',
      prompt: message,
      n: 1, //一次生成一条
      size: '256x256'
    })

    if (image.data) {
      ctx.body = { url: image.data[0].url }
    } else {
      console.log('//', image)
    }
  }
  // 文字转语音
  async textToVoice(ctx: Context, next: Next) {
    const { message } = ctx.request.body
    if (!message) return
    const voice = await openai.audio.speech.create({
      model: 'tts-1',
      input: message,
      voice: 'alloy'
    })
    ctx.set('Content-Type', 'audio/*')
    const buffer = Buffer.from(await voice.arrayBuffer())
    ctx.body = buffer
  }
  // 语音转文字，默认以当前语音语言作为输出，传入英文语言参数作为英文输出
  async voiceToText(ctx: Context, next: Next) {
    const { file } = ctx.request.files as { file: any }
    const { to } = ctx.request.body

    // transcriptions:语音生成文字；transcriptions：任意语言语音转英文文字
    const voice = await openai.audio[to ? 'translations' : 'transcriptions'].create({
      file: fs.createReadStream(fs.readFileSync(file.filepath)),
      model: 'whisper-1'
    })
    ctx.body = voice.text
  }

  //   图片想象
  async imagine(ctx: Context, next: Next) {
    const { text } = ctx.request.body
    const { file } = ctx.request.files as { file: any }

    const response = await openai.chat.completions.create({
      model: 'gpt-4o',
      messages: [
        {
          role: 'user',
          content: [
            { type: 'text', text },
            {
              type: 'image_url',
              image_url: {
                url: `data:image/png;base64,${fs.readFileSync(file.filepath).toString('base64')}`
              }
            }
          ]
        }
      ]
    })
    ctx.body = { message: response.choices[0].message.content }
    console.log(response.choices[0])
  }
}
export default new GptController()
